﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Windows.Forms;
using PPClasses;

namespace PortalPlan
{
    public partial class PictureMarker : UserControl
    {

        protected Bitmap GrayImage;

        public Problem TheData = new Problem {ThePoints = new List<Portal>(), TheImage = null};

        public enum Modes
        {
            Add,
            Delete,
            Move
        };

        private Modes _Mode;

        public delegate void ModeChangeDel(Modes mode);

        public ModeChangeDel ModeChanged;

        public Modes Mode
        {
            get { return _Mode; }
            set
            {
                _Mode = value;
                switch (_Mode)
                {
                    case Modes.Add:
                        this.Cursor = Cursors.Cross;
                        break;
                    case Modes.Move:
                        this.Cursor = Cursors.SizeAll;
                        break;
                    case Modes.Delete:
                        // Bitmap pb = new Bitmap(PortalPlan.Properties.Resources.DeleteHS);
                        PictureBox pb = new PictureBox() {Image = PortalPlan.Properties.Resources.DeleteHS};
                        this.Cursor = new Cursor(((Bitmap) pb.Image).GetHicon()); //Cursors.No;
                        break;
                }
                if (!this.DesignMode && ModeChanged != null && this.Created)
                    this.BeginInvoke(ModeChanged, _Mode);
            }
        }


        private bool Dragging = false;
        private int DraggingPointNum = -1;

        private List<Pair> PairList;
        private List<Pair> MustLink;


        public PictureMarker()
        {
            InitializeComponent();

            this.DoubleBuffered = true;
            this.SetStyle(ControlStyles.UserPaint |
                          ControlStyles.AllPaintingInWmPaint |
                          ControlStyles.ResizeRedraw |
                          ControlStyles.ContainerControl |
                          ControlStyles.OptimizedDoubleBuffer |
                          ControlStyles.SupportsTransparentBackColor, true);
        }

        public void UpdateWith(List<Pair> pairList, List<Pair> mustLink)
        {
            if (PairList != null)
            {
                lock (PairList)
                {
                    PairList = pairList;
                    MustLink = mustLink;
                }
            }
            else
            {
                PairList = pairList;
                MustLink = mustLink;
            }
            this.Invalidate();
        }

        private void PictureMarker_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            if (this.DesignMode)
            {
                g.FillRectangle(new SolidBrush(Color.LightGoldenrodYellow), new Rectangle(new Point(0, 0), this.Size));
                return;
            }

            if (TheData.TheImage == null)
            {
                g.FillRectangle(new SolidBrush(Color.White), new Rectangle(new Point(0, 0), this.Size));
            }
            else
            {
                if (GrayImage == null)
                    GrayImage = MakeGrayscale(new Bitmap(TheData.TheImage));
                g.DrawImage(GrayImage, 0, 0);
            }

            const float linkThickness = 3;
            g.SmoothingMode = SmoothingMode.HighQuality;

            if (PairList != null)
            {
                lock (PairList)
                {
                    foreach (Pair p in PairList)
                    {
                        bool must = MustLink != null && MustLink.FindIndex(x => x.same(p)) != -1;
                        if (p.a >= TheData.ThePoints.Count || p.b >= TheData.ThePoints.Count)
                            continue;
                        Portal pt1 = TheData.ThePoints[p.a];
                        Portal pt2 = TheData.ThePoints[p.b];
                        g.DrawLine(new Pen(must ? Color.PaleGreen : Color.ForestGreen, linkThickness), (int) pt1.X,
                                   (int) pt1.Y, (int) pt2.X, (int) pt2.Y);
                    }
                }
            }

            const int pSize = 8;
            Brush pointBrush = new SolidBrush(Color.LawnGreen);
            foreach (Portal p in TheData.ThePoints)
            {
                g.FillEllipse(pointBrush, (int) p.X - pSize, (int) p.Y - pSize, pSize * 2, pSize * 2);
            }
        }

        private void PictureMarker_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left)
                return;

            if (Mode == Modes.Add)
            {
                DraggingPointNum = TheData.ThePoints.Count;
                TheData.ThePoints.Add(new Portal(e.X, e.Y));
                UpdateWith(null, null);
                Dragging = true;
            }
            else
            {
                int n = FindNearbyPoint(new Portal(e.X, e.Y));
                if (n == -1)
                    return;

                if (Mode == Modes.Delete)
                {
                    TheData.ThePoints.RemoveAt(n);
                    UpdateWith(null, null);
                }
                else if (Mode == Modes.Move)
                {
                    Dragging = true;
                    DraggingPointNum = n;
                }
            }

            this.Invalidate();

        }

        private int FindNearbyPoint(Portal location)
        {
            double nearest = double.MaxValue;
            int r = -1;
            for (int i = 0; i < TheData.ThePoints.Count; i++)
            {
                double d = location.DistanceTo(TheData.ThePoints[i]);
                if (d < nearest)
                {
                    nearest = d;
                    r = i;
                }

            }
            if (nearest < 8)
                return r;
            return -1;
        }

        private void PictureMarker_MouseMove(object sender, MouseEventArgs e)
        {
            if (!Dragging || DraggingPointNum == -1)
                return;

            TheData.ThePoints[DraggingPointNum] = new Portal(e.X, e.Y);

            if (!Solver.SolutionOK(TheData.ThePoints, PairList))
                UpdateWith(null, null);
            else
            {
                this.Invalidate();
            }
        }

        private void PictureMarker_MouseUp(object sender, MouseEventArgs e)
        {
            Dragging = false;
            DraggingPointNum = -1;
        }

        // From http://tech.pro/tutorial/660/csharp-tutorial-convert-a-color-image-to-grayscale
        public static Bitmap MakeGrayscale(Bitmap original)
        {
            //create a blank bitmap the same size as original
            Bitmap newBitmap = new Bitmap(original.Width, original.Height);

            //get a graphics object from the new image
            Graphics g = Graphics.FromImage(newBitmap);

            //create the grayscale ColorMatrix
            ColorMatrix colorMatrix = new ColorMatrix(
                new float[][]
                    {
                        new float[] {.3f, .3f, .3f, 0, 0},
                        new float[] {.59f, .59f, .59f, 0, 0},
                        new float[] {.11f, .11f, .11f, 0, 0},
                        new float[] {0, 0, 0, 1, 0},
                        new float[] {0, 0, 0, 0, 1}
                    });

            //create some image attributes
            ImageAttributes attributes = new ImageAttributes();

            //set the color matrix attribute
            attributes.SetColorMatrix(colorMatrix);

            //draw the original image on the new image
            //using the grayscale color matrix
            g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
                        0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);

            //dispose the Graphics object
            g.Dispose();
            return newBitmap;
        }

        public void SetImage(Image img)
        {
            if (img == null)
            {
                TheData.TheImage = null;
                GrayImage = null;
            }
            else
            {
                TheData.TheImage = new Bitmap(img);
                GrayImage = null;
            }
            this.Invalidate();
        }

        /*
        private int ColorDist(Color p, Color c)
        {
            int r = p.R - c.R;
            int g = p.G - c.G;
            int b = p.B - c.B;
            int d = r * r + g * g + b * b;
            return d;

        }

        public void GuessPortals()
        {
            const int nearness = 11000;
            TheData.ThePoints = new List<Portal>();
            using (Image i2 = (Image) TheData.TheImage.Clone())
            {
                using (Bitmap b = new Bitmap(i2))
                {
                    for (int x = 0; x < b.Width; x++)
                    {
                        for (int y = 0; y < b.Height; y++)
                        {
                            Color[] colors = new[]
                                {
                                    Color.FromArgb(0, 255, 0),
                                    Color.FromArgb(0, 220, 0),
                                    Color.FromArgb(0, 182, 255),
                                    Color.FromArgb(0, 136, 255),
                                };

                            Color p = b.GetPixel(x, y);
                            if (p.R == 0 && p.G == 0 && p.B == 0)
                                continue;

                            int closestD = int.MaxValue;
                            Color closestC = Color.Black;
                            foreach (Color c in colors)
                            {
                                int d = ColorDist(p, c);
                                if (d < closestD)
                                {
                                    closestD = d;
                                    closestC = c;
                                }

                            }
                            if (closestD > nearness)
                                continue;

                            Queue<System.Drawing.Point> queue = new Queue<Point>();
                            queue.Enqueue(new Point(x, y));
                            b.SetPixel(x, y, Color.Black);
                            int ax = 0, ay = 0, n = 0;
                            while (queue.Count > 0)
                            {
                                Point xy = queue.Dequeue();
                                for (int x2 = xy.X - 1; x2 <= xy.X + 1; x2++)
                                {
                                    for (int y2 = xy.Y - 1; y2 <= xy.Y + 1; y2++)
                                    {
                                        if (x2 == x && y2 == y || x2 < 0 || x2 >= b.Width || y2 < 0 || y2 >= b.Height)
                                            continue;
                                        Color p2 = b.GetPixel(x2, y2);
                                        if (p2.R == 0 && p2.G == 0 && p2.B == 0)
                                            continue;
                                        if (ColorDist(p2, closestC) <= nearness)
                                        {
                                            queue.Enqueue(new Point(x2, y2));
                                            b.SetPixel(x2, y2, Color.Black);
                                        }
                                    }
                                }
                                ax += xy.X;
                                ay += xy.Y;
                                n++;
                            }
                            TheData.ThePoints.Add(new Portal((double) ax / n, (double) ay / n));
                            break;
                        }
                    }
                }
            }
    }*/

    }
}
